001 /*
002 * Copyright 2006 Stephen J. McConnell.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.metro.builder;
020
021 import java.io.IOException;
022 import java.net.URL;
023 import java.net.URI;
024 import java.net.URISyntaxException;
025 import java.util.Properties;
026 import java.beans.IntrospectionException;
027
028 import net.dpml.lang.Version;
029
030 import net.dpml.metro.info.Type;
031 import net.dpml.metro.info.InfoDescriptor;
032 import net.dpml.metro.info.ContextDescriptor;
033 import net.dpml.metro.info.CategoryDescriptor;
034 import net.dpml.metro.info.ServiceDescriptor;
035 import net.dpml.metro.info.EntryDescriptor;
036 import net.dpml.metro.info.CollectionPolicy;
037 import net.dpml.metro.info.LifestylePolicy;
038 import net.dpml.metro.info.ThreadSafePolicy;
039 import net.dpml.metro.info.Priority;
040
041 import net.dpml.state.State;
042 import net.dpml.state.StateDecoder;
043 import net.dpml.state.DefaultState;
044
045 import net.dpml.util.DOM3DocumentBuilder;
046 import net.dpml.util.DecodingException;
047 import net.dpml.util.ElementHelper;
048 import net.dpml.util.Resolver;
049
050 import org.w3c.dom.Element;
051 import org.w3c.dom.Document;
052
053
054 /**
055 * Component type decoder.
056 *
057 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
058 * @version 1.0.1
059 */
060 public final class ComponentTypeDecoder
061 {
062 private static final StateDecoder STATE_DECODER = new StateDecoder();
063
064 private static final DOM3DocumentBuilder DOCUMENT_BUILDER = new DOM3DocumentBuilder();
065
066 /**
067 * Load a type.
068 * @param subject the component implementation class
069 * @param resolver build-time value resolver
070 * @return the component type descriptor
071 * @exception IOException if an error occurs reading the type definition
072 * @exception IntrospectionException if an introspection error occurs
073 */
074 public Type loadType( Class subject, Resolver resolver ) throws IOException, IntrospectionException
075 {
076 String classname = subject.getName();
077 String path = classname.replace( '.', '/' );
078 String target = path + ".type";
079 ClassLoader classloader = subject.getClassLoader();
080 URL url = classloader.getResource( target );
081 if( null == url )
082 {
083 return Type.createType( subject );
084 }
085 else
086 {
087 try
088 {
089 URI uri = new URI( url.toString() );
090 return loadType( uri, resolver );
091 }
092 catch( URISyntaxException e )
093 {
094 final String error =
095 "Bad uri value."
096 + "\nURI: " + url;
097 IOException exception = new IOException( error );
098 exception.initCause( e );
099 throw exception;
100 }
101 }
102 }
103
104 /**
105 * Load a type.
106 * @param uri the component type source uri
107 * @param resolver build-time uri resolver
108 * @return the component type descriptor
109 * @exception IOException if an error occurs reading the type definition
110 */
111 public Type loadType( URI uri, Resolver resolver ) throws IOException
112 {
113 try
114 {
115 final Document document = DOCUMENT_BUILDER.parse( uri );
116 final Element root = document.getDocumentElement();
117 return buildType( root, resolver );
118 }
119 catch( Throwable e )
120 {
121 final String error =
122 "An error while attempting to load a type."
123 + "\nType URI: " + uri;
124 IOException exception = new IOException( error );
125 exception.initCause( e );
126 throw exception;
127 }
128 }
129
130 private Type buildType( Element root, Resolver resolver ) throws Exception
131 {
132 InfoDescriptor info = buildNestedInfoDescriptor( root );
133 ServiceDescriptor[] services = buildNestedServices( root );
134 ContextDescriptor context = buildNestedContext( root );
135 CategoryDescriptor[] categories = buildNestedCategories( root );
136 State state = buildNestedState( root );
137 return new Type( info, categories, context, services, state );
138 }
139
140 private InfoDescriptor buildNestedInfoDescriptor( Element root ) throws DecodingException
141 {
142 Element info = ElementHelper.getChild( root, "info" );
143 if( null == info )
144 {
145 final String error =
146 "Definition of <type> is missing the required <info> element.";
147 throw new DecodingException( root, error );
148 }
149
150 String name = ElementHelper.getAttribute( info, "name" );
151 String classname = ElementHelper.getAttribute( info, "class" );
152 String version = ElementHelper.getAttribute( info, "version" );
153 String collection = ElementHelper.getAttribute( info, "collection", "system" );
154 String lifestyle = ElementHelper.getAttribute( info, "lifestyle", null );
155 String threadsafe = ElementHelper.getAttribute( info, "threadsafe", "unknown" );
156 Properties properties = buildNestedProperties( info );
157
158 return new InfoDescriptor(
159 name,
160 classname,
161 Version.parse( version ),
162 buildLifestylePolicy( lifestyle ),
163 CollectionPolicy.parse( collection ),
164 ThreadSafePolicy.parse( threadsafe ),
165 properties );
166 }
167
168 private LifestylePolicy buildLifestylePolicy( String lifestyle )
169 {
170 if( null == lifestyle )
171 {
172 return null;
173 }
174 else
175 {
176 return LifestylePolicy.parse( lifestyle );
177 }
178 }
179
180 private ServiceDescriptor[] buildNestedServices( Element root )
181 {
182 Element element = ElementHelper.getChild( root, "services" );
183 if( null == element )
184 {
185 return new ServiceDescriptor[0];
186 }
187 else
188 {
189 Element[] children = ElementHelper.getChildren( element, "service" );
190 ServiceDescriptor[] services = new ServiceDescriptor[ children.length ];
191 for( int i=0; i<children.length; i++ )
192 {
193 Element child = children[i];
194 String classname = ElementHelper.getAttribute( child, "class" );
195 String version = ElementHelper.getAttribute( child, "version", "1.0.0" );
196 services[i] = new ServiceDescriptor( classname, Version.parse( version ) );
197 }
198 return services;
199 }
200 }
201
202 private ContextDescriptor buildNestedContext( Element root )
203 {
204 Element context = ElementHelper.getChild( root, "context" );
205 if( null == context )
206 {
207 return new ContextDescriptor( new EntryDescriptor[0] );
208 }
209 else
210 {
211 Element[] children = ElementHelper.getChildren( context, "entry" );
212 EntryDescriptor[] entries = new EntryDescriptor[ children.length ];
213 for( int i=0; i<children.length; i++ )
214 {
215 Element child = children[i];
216 String key = ElementHelper.getAttribute( child, "key" );
217 String classname = ElementHelper.getAttribute( child, "class" );
218 boolean optional = ElementHelper.getBooleanAttribute( child, "optional", false );
219 boolean isVolatile = ElementHelper.getBooleanAttribute( child, "volatile", false );
220 entries[i] = new EntryDescriptor( key, classname, optional, isVolatile );
221 }
222 return new ContextDescriptor( entries );
223 }
224 }
225
226 private CategoryDescriptor[] buildNestedCategories( Element root )
227 {
228 Element element = ElementHelper.getChild( root, "categories" );
229 if( null == element )
230 {
231 return new CategoryDescriptor[0];
232 }
233 else
234 {
235 Element[] children = ElementHelper.getChildren( element, "category" );
236 CategoryDescriptor[] categories = new CategoryDescriptor[ children.length ];
237 for( int i=0; i<children.length; i++ )
238 {
239 Element elem = children[i];
240 String name = ElementHelper.getAttribute( elem, "name" );
241 String priority = ElementHelper.getAttribute( elem, "priority" );
242 Properties properties = buildNestedProperties( elem );
243 categories[i] = new CategoryDescriptor( name, Priority.parse( priority ), properties );
244 }
245 return categories;
246 }
247 }
248
249 private State buildNestedState( Element root )
250 {
251 Element element = ElementHelper.getChild( root, "state" );
252 if( null == element )
253 {
254 return new DefaultState();
255 }
256 else
257 {
258 return STATE_DECODER.buildStateGraph( element );
259 }
260 }
261
262 private Properties buildNestedProperties( Element element )
263 {
264 Element[] entries = ElementHelper.getChildren( element, "property" );
265 if( entries.length == 0 )
266 {
267 return null;
268 }
269 else
270 {
271 Properties properties = new Properties();
272 for( int i=0; i<entries.length; i++ )
273 {
274 Element elem = entries[i];
275 String name = ElementHelper.getAttribute( elem, "name" );
276 String value = ElementHelper.getAttribute( elem, "value" );
277 properties.setProperty( name, value );
278 }
279 return properties;
280 }
281 }
282
283 }
284